package com.kingombo.jcl5j;

import java.util.Formatter;

public class Log extends LogFactory implements org.apache.commons.logging.Log{

    private static ThreadLocal<Formatter> formatterCache = new FormatterCache();

    private org.apache.commons.logging.Log log = null;

    public Log(org.apache.commons.logging.Log log) {
        this.log = log;
    }

    @Override
    public void debug(Object obj) {
        log.debug(obj);
    }

    @Override
    public void debug(Object obj, Throwable throwable) {
        log.debug(obj, throwable);
    }

    @Override
    public void error(Object obj) {
        log.error(obj);
    }

    @Override
    public void error(Object obj, Throwable throwable) {
        log.error(obj,throwable);
    }

    @Override
    public void fatal(Object obj) {
        log.fatal(obj);
    }

    @Override
    public void fatal(Object obj, Throwable throwable) {
        log.fatal(obj, throwable);
    }

    @Override
    public void info(Object obj) {
        log.info(obj);
    }

    @Override
    public void info(Object obj, Throwable throwable) {
        log.info(obj,throwable);
    }

    @Override
    public boolean isDebugEnabled() {

        return log.isDebugEnabled();

    }

    @Override
    public boolean isErrorEnabled() {

        return log.isErrorEnabled();

    }

    @Override
    public boolean isFatalEnabled() {

        return log.isFatalEnabled();

    }

    @Override
    public boolean isInfoEnabled() {

        return log.isInfoEnabled();

    }

    @Override
    public boolean isTraceEnabled() {

        return log.isTraceEnabled();
    }

    @Override
    public boolean isWarnEnabled() {

        return log.isWarnEnabled();
    }

    @Override
    public void trace(Object obj) {

        log.trace(obj);
    }

    @Override
    public void trace(Object obj, Throwable throwable) {

      log.trace(obj, throwable);
    }

    @Override
    public void warn(Object obj) {

        log.warn(obj);
    }

    @Override
    public void warn(Object obj, Throwable throwable) {

        log.warn(obj, throwable);
    }

    public void trace(String format, Object... args) {

        if (!log.isTraceEnabled()) {
            return;
        }

        log.trace(sprintf(format, args));
    }

    public void trace(String format, Throwable t, Object... args) {

        if (!log.isTraceEnabled())
            return;

        log.trace(sprintf(format, args), t);
    }

    public void debug(String format, Object... args) {

        if (!log.isDebugEnabled()) {
            return;
        }

        log.debug(sprintf(format, args));
    }

    public void debug(String format, Throwable t, Object... args) {

        if (!log.isDebugEnabled())
            return;

        log.debug(sprintf(format, args), t);
    }

    public void info(String format, Object... args) {

        if (!log.isInfoEnabled()) {
            return;
        }

        log.info(sprintf(format, args));
    }

    public void info(String format, Throwable t, Object... args) {

        if (!log.isInfoEnabled())
            return;

        log.info(sprintf(format, args), t);
    }

    public void warn(String format, Object... args) {

        if (!log.isWarnEnabled()) {
            return;
        }

        log.warn(sprintf(format, args));
    }

    public void warn(String format, Throwable t, Object... args) {

        if (!log.isWarnEnabled())
            return;

        log.warn(sprintf(format, args), t);
    }

    public void error(String format, Object... args) {

        if (!log.isErrorEnabled()) {
            return;
        }

        log.error(sprintf(format, args));
    }

    public void error(String format, Throwable t, Object... args) {

        if (!log.isErrorEnabled())
            return;

        log.error(sprintf(format, args), t);
    }

    public void fatal(String format, Object... args) {

        if (!log.isFatalEnabled()) {
            return;
        }

        log.fatal(sprintf(format, args));
    }

    public void fatal(String format, Throwable t, Object... args) {

        if (!log.isFatalEnabled())
            return;

        log.fatal(sprintf(format, args), t);
    }


    private static String sprintf(String format, Object[] args) {

        Formatter formatter = getFormatter();
        formatter.format(format, args);

        StringBuilder sb = (StringBuilder) formatter.out();
        String message = sb.toString();
        sb.setLength(0);

        return message;
    }

    private static Formatter getFormatter() {
        return formatterCache.get();
    }

    public void traceMethodStart(Object... args) {
        if (!log.isTraceEnabled())
            return;
        String method = Thread.currentThread().getStackTrace()[2].getMethodName();
        StringBuilder sb = new StringBuilder();
        sb.append(method);
        sb.append("(");
        for (int i = 0; i < args.length; i++) {
            sb.append(args[i]);
            if(i != args.length-1)
            sb.append(",");
        }
        sb.append(") is called");
        log.trace(sb.toString());
    }

    public void traceMethodEnd() {
        if (!log.isTraceEnabled())
            return;
        String method = Thread.currentThread().getStackTrace()[2].getMethodName();
        StringBuilder sb = new StringBuilder();
        sb.append(method);
        sb.append(" is returned");
        log.trace(sb.toString());
    }
}

class FormatterCache extends ThreadLocal<Formatter> {

    protected synchronized Formatter initialValue() {
        return new Formatter();
    }

}